package com.gotye.particlesystem.gles;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import com.gotye.particlesystem.R;
import com.gotye.particlesystem.objects.Ball;
import com.gotye.particlesystem.objects.Cube;
import com.gotye.particlesystem.objects.CommonObject;
import com.gotye.particlesystem.programs.ColorShaderProgram;
import com.gotye.particlesystem.programs.TextureShaderProgram;
import com.gotye.particlesystem.util.Geometry.Vector;
import com.gotye.particlesystem.util.TextureHelper;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by Michael.Ma on 2016/2/1.
 */
public class GLRenderer implements GLSurfaceView.Renderer {
    private Context mContext;

    private long frames = 0L;

    private final float[] projectionMatrix = new float[16];
    private final float[] viewMatrix = new float[16];
    private final float[] modelMatrix = new float[16];
    private final float[] viewProjectionMatrix = new float[16];
    private final float[] modelViewProjectionMatrix = new float[16];
    private final float[] modelViewMatrix = new float[16];

    private float[] resultVector = new float[4];
    private float[] lightVector = new float[4];
    private float lightDelta = -0.02f;

    private int texture;
    private TextureShaderProgram textureProgram;
    private ColorShaderProgram colorProgram;
    private Cube cube;
    private Ball ball;
    private CommonObject object;

    private float angleX = 0f;
    private float angleY = 0f;
    private float deltaX = 1f;

    private float lookatDistance = 2f;

    private boolean bLineFrame = false;

    private final Vector vectorLight = new Vector(0f, 0.5f, 1f);

    public GLRenderer(Context context) {
        mContext = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        textureProgram = new TextureShaderProgram(mContext);
        colorProgram = new ColorShaderProgram(mContext);

        cube = new Cube();
        ball = new Ball(1f);
        object = new CommonObject(1);
        texture = TextureHelper.loadTexture(mContext, R.drawable.popeye);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);

        //MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / (float) height, 1f, 10f);
        Matrix.setIdentityM(projectionMatrix, 0);
        Matrix.perspectiveM(projectionMatrix, 0, 45, (float) width / (float) height, 0.1f, 10f);

        Matrix.setIdentityM(viewMatrix, 0);
        //Matrix.translateM(viewMatrix, 0, 0f, 0, -7f);
        Matrix.setLookAtM(viewMatrix, 0,
                0f, 0f, lookatDistance,
                0f, 0f, 0f,
                0f, 1f, 0f);
        Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);

        lightVector[0] = vectorLight.x;
        lightVector[1] = vectorLight.y;
        lightVector[2] = vectorLight.z;
        lightVector[3] = 1f;

        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        GLES20.glEnable(GLES20.GL_CULL_FACE);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // draw light source
        positionLightInScene();

        colorProgram.useProgram();
        colorProgram.setUniforms(modelViewProjectionMatrix, 1f, 1f, 1f);

        ball.bindData(colorProgram);
        ball.draw();

        // draw object
        positionObjectInScene();

        lightVector[1] += lightDelta;
        if (lightVector[1] < -0.5f || lightVector[1] > 0.5f)
            lightDelta = -lightDelta;
        Matrix.multiplyMV(resultVector, 0,
                viewMatrix, 0,
                lightVector, 0);

        Vector newPos = new Vector(resultVector[0], resultVector[1], resultVector[2]);

        textureProgram.useProgram();
        textureProgram.setUniforms(modelViewProjectionMatrix, modelViewMatrix,
                texture, newPos);

        object.bindData(textureProgram);
        object.draw();
        //cube.draw();

        frames++;
    }

    public void rotate(float deltaX, float deltaY, float deltaZ) {
        angleX += deltaX;
        angleY += deltaY;
    }

    public void scale(float ratio) {
        lookatDistance /= ratio;

        Matrix.setIdentityM(viewMatrix, 0);
        //Matrix.translateM(viewMatrix, 0, 0f, 0, -7f);
        Matrix.setLookAtM(viewMatrix, 0,
                0f, 0f, lookatDistance,
                0f, 0f, 0f,
                0f, 1f, 0f);
        Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
    }

    public void toggleDisplay() {
        bLineFrame = !bLineFrame;
        //ball.setLineFrame(bLineFrame);
    }

    private void positionObjectInScene() {
        /*angleX += deltaX;
        if (angleX >= 60f || angleX <= -60f)
            deltaX = -deltaX;*/

        /*angleY += 1f;
        if (angleY >= 360f)
            angleY -= 360f;*/

        Matrix.setIdentityM(modelMatrix, 0);
        Matrix.rotateM(modelMatrix, 0, angleX, 1f, 0f, 0f);
        Matrix.rotateM(modelMatrix, 0, angleY, 0f, 1f, 0f);
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, viewProjectionMatrix, 0,
                modelMatrix, 0);

        Matrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0,
                modelMatrix, 0);
    }

    private void positionLightInScene() {
        Matrix.setIdentityM(modelMatrix, 0);
        Matrix.translateM(modelMatrix, 0, lightVector[0], lightVector[1], lightVector[2]);
        Matrix.scaleM(modelMatrix, 0, 0.03f, 0.03f, 0.03f);
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, viewProjectionMatrix, 0,
                modelMatrix, 0);
    }
}
